package br.ufpe.cin.imlm2.jbeat.model;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A lightweight representation of a method.
 * It contains the minimal amount of information necessary to build
 * exception paths and chains. <br>
 * A method may: <br>
 * 1 - Throw an exception explicitly. <br>
 * 2 - Generate an exception (e.g. bytecode instruction raising an exception). <br>
 * 3 - Propagate an exception coming from a method call.<br>
 * 4 - Rethrow an exception that was captured and raised again.<br>
 * 5 - Trap an exception.<br>
 * 
 * 1,2,3 and 4 form the "throwable set" of a method and
 * 5 is called the "trapped set" of a method
 * @author Irineu Martins
 *
 */
public class LightWeightMethod implements Serializable{

	private static final long serialVersionUID = -1940211535787442979L;
	
	private ILightWeightType returnType;
	private LightWeightMethodSignature signature;
	private Set<LightWeightClass> generatedSet;
	private Set<LightWeightClass> thrownSet;
	private Map<LightWeightClass,List<LightWeightMethod>> propagatedMap;
	private Map<LightWeightClass,List<LightWeightMethod>> rethrownMap;
	private Map<LightWeightClass, List<LightWeightMethod>> trappedMap;
	private Set<LightWeightMethod> propagatesToSet;
	private LightWeightClass declaringClass;
	
	public LightWeightMethod(ILightWeightType returnType,
			List<ILightWeightType> parameters,
			Set<LightWeightClass> generatedSet,
			Set<LightWeightClass> thrownSet,
			Map<LightWeightClass, List<LightWeightMethod>> propagatedMap,
			Map<LightWeightClass, List<LightWeightMethod>> rethrownMap,
			Map<LightWeightClass, List<LightWeightMethod>> trappedSet,
			Set<LightWeightMethod> propsToSet,String name,LightWeightClass decClass) {
		super();
		this.returnType = returnType;
		this.signature = new LightWeightMethodSignature(parameters, name);
		this.generatedSet = generatedSet;
		this.thrownSet = thrownSet;
		this.propagatedMap = propagatedMap;
		this.rethrownMap = rethrownMap;
		this.trappedMap = trappedSet;
		this.propagatesToSet = propsToSet;
		this.declaringClass = decClass;
	}
	
	

	public LightWeightMethod(List<ILightWeightType> parameters, String name,LightWeightClass decClass) {
		this(null,parameters, new HashSet<LightWeightClass>(),new HashSet<LightWeightClass>(), 
				new HashMap<LightWeightClass, List<LightWeightMethod>>(),
				new HashMap<LightWeightClass, List<LightWeightMethod>>(),
				new HashMap<LightWeightClass, List<LightWeightMethod>>(),
				new HashSet<LightWeightMethod>(), name,decClass);
		
	}



	public ILightWeightType getReturnType() {
		return returnType;
	}

	public List<ILightWeightType> getParameters() {
		return this.signature.getParameters();
	}

	public Set<LightWeightClass> getGeneratedSet() {
		return generatedSet;
	}

	public Set<LightWeightClass> getThrownSet() {
		return thrownSet;
	}

	public Map<LightWeightClass, List<LightWeightMethod>> getRethrownMap() {
		return rethrownMap;
	}

	public Map<LightWeightClass, List<LightWeightMethod>> getPropagatedMap() {
		return propagatedMap;
	}

	public Map<LightWeightClass, List<LightWeightMethod>> getTrappedMap() {
		return trappedMap;
	}
	
	public final Set<LightWeightMethod> getPropagatesToSet() {
		return propagatesToSet;
	}

	public String getName() {
		return this.signature.getName();
	}

	public LightWeightClass getDeclaringClass() {
		return declaringClass;
	}

	public LightWeightMethodSignature getSignature() {
		return signature;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(this.getDeclaringClass().toString()).append('.').append(this.getName());
		sb.append('(');
		for (ILightWeightType t : this.signature.getParameters()) {
			sb.append(t.toString()).append(',');
		}
		if(this.signature.getParameters().size() > 0){
			sb.deleteCharAt(sb.length() - 1);
		}
		sb.append(')');
		return sb.toString();
	}
	
	public static final class LightWeightMethodSignature implements Serializable{
		
		
		private static final long serialVersionUID = 1L;
		
		private List<ILightWeightType> parameters;
		private String name;
		
		private LightWeightMethodSignature(List<ILightWeightType> parameters,
				String name) {
			super();
			this.parameters = parameters;
			this.name = name;
		}

		@Override
		public boolean equals(Object obj) {
			if(obj instanceof LightWeightMethodSignature) {
				LightWeightMethodSignature otherSig = (LightWeightMethodSignature) obj;
				if(this.name.equals(otherSig.name)) {
					if(this.parameters.size() == otherSig.parameters.size()){
						Iterator<ILightWeightType> it = this.parameters.iterator();
						Iterator<ILightWeightType> otherIt = otherSig.parameters.iterator();
						while(it.hasNext() && otherIt.hasNext()){
							ILightWeightType t1 = it.next();
							ILightWeightType t2 = otherIt.next();
							if(!t1.equals(t2))
								return false;
						}
						return true; 
					}
				}
			}
			return false;
		}

		
		
		@Override
		public int hashCode() {
			return 0;
		}

		public List<ILightWeightType> getParameters() {
			return parameters;
		}

		public void setParameters(List<ILightWeightType> parameters) {
			this.parameters = parameters;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
				
		
	}
	
}
